﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto.Paddings;

namespace DotNet.Helper.Scoket
{
    /// <inheritdoc />
    /// <summary>
    /// 异步TCP客户端帮助类
    /// </summary>
    public class AsyncTcpClientHelper : IDisposable
    {
        #region 字段
        /// <summary>
        /// TcpClient对象
        /// </summary>
        private TcpClient _tcpClient;
        /// <summary>
        /// 是否被回收
        /// </summary>
        private bool _disposed = false;
        #endregion

        #region 构造函数
        /// <summary>
        /// 异步TCP客户端帮助类
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        public AsyncTcpClientHelper(string host, int port)
        {
            this.Host = host;
            this.Port = port;
            this._tcpClient = new TcpClient();
        }
        #endregion

        #region 属性
        /// <summary>
        /// 远程服务的IP地址
        /// </summary>
        public string Host { get; private set; }
        /// <summary>
        /// 远程服务的端口
        /// </summary>
        public int Port { get; private set; }
        /// <summary>
        /// 是否与服务器建立连接
        /// </summary>
        public bool Connected => _tcpClient.Client.Connected;
        #endregion

        #region 连接
        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <returns></returns>
        public AsyncTcpClientHelper Connect()
        {
            if (!Connected)
            {
                _tcpClient.BeginConnect(Host, Port, HandlerTcpServerConnected, _tcpClient);
            }
            return this;
        }

        /// <summary>
        /// 关闭与服务器的连接
        /// </summary>
        /// <returns></returns>
        public AsyncTcpClientHelper Close()
        {
            if (Connected)
            {
                _tcpClient.Close();
                _tcpClient = new TcpClient();
            }
            return this;
        }
        #endregion

        #region 接收
        /// <summary>
        /// 连接服务器成功后执行
        /// </summary>
        /// <param name="ar"></param>
        private void HandlerTcpServerConnected(IAsyncResult ar)
        {
            try
            {
                _tcpClient.EndConnect(ar);
                RaiseServerConnected(Host, Port);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                RaiseServerExceptionOccurred(Host, Port, ex);
                return;
            }

            //连接成功，等待服务器回应
            byte[] buffer = new byte[_tcpClient.ReceiveBufferSize];
            _tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, HandlerDatagramReceived, buffer);
        }

        /// <summary>
        /// 读取回应包执行
        /// </summary>
        /// <param name="ar"></param>
        private void HandlerDatagramReceived(IAsyncResult ar)
        {
            NetworkStream stream = _tcpClient.GetStream();
            int numberOfReadBytes = 0;

            try
            {
                numberOfReadBytes = stream.EndRead(ar);
            }
            catch (Exception ex)
            {
                numberOfReadBytes = 0;
                Close();
                Console.WriteLine(ex);
            }

            if (numberOfReadBytes == 0)
            {
                return;
            }

            //接受流并通知
            byte[] buffer = ar.AsyncState as byte[];
            byte[] receiveBytes = new byte[numberOfReadBytes];
            Buffer.BlockCopy(buffer, 0, receiveBytes, 0, numberOfReadBytes);
            //通知接收二进制流的事件
            RaiseDatagramReceived(_tcpClient, receiveBytes);
            //继续读取服务器回包
            stream.BeginRead(buffer, 0, buffer.Length, HandlerDatagramReceived, buffer);

        }
        #endregion

        #region 事件
        /// <summary>
        /// 与服务器连接已建立事件
        /// </summary>
        public event EventHandler<TcpServerConnectedEventArgs> ServerConnected;
        /// <summary>
        /// 与服务器连接已断开事件
        /// </summary>
        public event EventHandler<TcpServerDisconnectedEventArgs> ServerDisconnected;
        /// <summary>
        /// 接收到数据报文事件
        /// </summary>
        public event EventHandler<TcpDatagramReceivedEventArgs> DatagramReceived;
        /// <summary>
        /// 与服务器连接发生异常事件
        /// </summary>
        public event EventHandler<TcpServerExceptionOccurredEventArgs> ServerExceptionOccurred;

        /// <summary>
        /// 通知订阅者已与服务器建立连接
        /// </summary>
        /// <param name="ipAddress">地址</param>
        /// <param name="port">端口</param>
        private void RaiseServerConnected(string ipAddress, int port)
        {
            ServerConnected?.Invoke(this, new TcpServerConnectedEventArgs(ipAddress, port));
        }

        /// <summary>
        /// 通知订阅者已与服务器断开连接
        /// </summary>
        /// <param name="ipAddress">地址</param>
        /// <param name="port">端口</param>
        private void RaiseServerDisconnected(string ipAddress, int port)
        {
            ServerDisconnected?.Invoke(this, new TcpServerDisconnectedEventArgs(ipAddress, port));
        }

        /// <summary>
        /// 通知订阅者已从服务器接收到数据流
        /// </summary>
        /// <param name="tcpClient">TcpClient对象</param>
        /// <param name="datagram">接收到的数据</param>
        private void RaiseDatagramReceived(TcpClient tcpClient, byte[] datagram)
        {
            DatagramReceived?.Invoke(this, new TcpDatagramReceivedEventArgs(tcpClient, datagram));
        }

        /// <summary>
        /// 通知订阅者服务发生异常
        /// </summary>
        /// <param name="ipAddress">地址</param>
        /// <param name="port">端口</param>
        /// <param name="exception">异常信息</param>
        private void RaiseServerExceptionOccurred(string ipAddress, int port, Exception exception)
        {
            ServerExceptionOccurred?.Invoke(this, new TcpServerExceptionOccurredEventArgs(ipAddress, port, exception));
        }
        #endregion

        #region 发送
        /// <summary>
        /// 发送报文
        /// </summary>
        /// <param name="datagram">报文</param>
        public void Send(byte[] datagram)
        {
            if(datagram == null)
                throw new ArgumentException("datagram");

            if (!Connected)
            {
                RaiseServerDisconnected(Host, Port);
                throw new InvalidProgramException("This client has not connected to server.");
            }

            _tcpClient.GetStream().BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, _tcpClient);
        }

        /// <summary>
        /// 开始写入流
        /// </summary>
        /// <param name="ar"></param>
        private void HandleDatagramWritten(IAsyncResult ar)
        {
            ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
        }
        #endregion

        #region 关闭
        /// <inheritdoc />
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    try
                    {

                    }
                    catch (Exception e)
                    {

                    }
                }

                _disposed = true;
            }
        }
        #endregion

    }
}
